﻿using System;
using System.Collections.Generic;
using System.Peppers.Win32;
using System.Runtime.InteropServices;
using System.Text;
using Microsoft.Win32;

namespace System.Peppers.ShellExtensions.Public
{
    /// <summary>
    /// Class for implementing an IURLSearchHook
    /// </summary>
    public abstract class UrlSearchHookBase : IUrlSearchHook
    {
        private const string REG_SHELL_EXT = @"SOFTWARE\Microsoft\Internet Explorer\UrlSearchHooks";

        /// <summary>
        /// Event fired when browser gets an unknown request in address bar
        /// </summary>
        public EventHandler<UrlSearchHookEventArgs> TranslateUrl;

        #region IUrlSearchHook Members

        uint IUrlSearchHook.Translate(ref string lpwszSearchURL, ref uint cchBufferSize)
        {
            bool success = true;
            try
            {
                UrlSearchHookEventArgs e = new UrlSearchHookEventArgs { Url = lpwszSearchURL };
                if (TranslateUrl != null)
                {
                    TranslateUrl(this, e);
                }

                success = e.Success;
                if (success)
                {
                    lpwszSearchURL = e.Url;
                }
                cchBufferSize = (uint)lpwszSearchURL.Length;
            }
            catch
            {
                //Handle this in case the derived class throws an error
                return (uint)HRESULT.E_FAIL;
            }

            return (uint)(success ? HRESULT.S_OK : HRESULT.E_FAIL);
        }

        #endregion

        #region COM Registration
        /// <summary>
        /// Call this from your COM registration fuction in a derived class
        /// </summary>
        /// <param name="type">Type of derived class</param>
        protected static void RegisterExtension(Type type)
        {
            GuidAttribute guidAttr = null;
            ShellExtensionAttribute extAttr = null;
            ShellExtensionHelper.ReflectAttributes(type, out guidAttr, out extAttr);

            if (guidAttr == null || extAttr == null || string.IsNullOrEmpty(extAttr.Name) || extAttr.Data == null)
            {
                throw new ArgumentException("Invalid attributes on type.");
            }
            Guid id = new Guid(guidAttr.Value);
            if (id == Guid.Empty)
            {
                throw new ArgumentException("Invalid attributes on type.");
            }

            try
            {
                string guid = id.ToString("B").ToUpper();

                using (RegistryKey key = Registry.LocalMachine.CreateSubKey(REG_SHELL_EXT))
                {
                    key.SetValue(guid, extAttr.Name);
                }
            }
            catch (Exception e)
            {
                Console.Error.WriteLine(e.ToString());
            }
        }

        /// <summary>
        /// Call this from your COM registration fuction in a derived class
        /// </summary>
        /// <param name="type">Type of derived class</param>
        protected static void UnregisterExtension(Type type)
        {
            GuidAttribute guidAttr = null;
            ShellExtensionAttribute extAttr = null;
            ShellExtensionHelper.ReflectAttributes(type, out guidAttr, out extAttr);

            if (guidAttr == null || extAttr == null || string.IsNullOrEmpty(extAttr.Name) || extAttr.Data == null)
            {
                throw new ArgumentException("Invalid attributes on type.");
            }
            Guid id = new Guid(guidAttr.Value);
            if (id == Guid.Empty)
            {
                throw new ArgumentException("Invalid attributes on type.");
            }

            try
            {
                string guid = id.ToString("B").ToUpper();

                using (RegistryKey key = Registry.LocalMachine.OpenSubKey(REG_SHELL_EXT, true))
                {
                    key.DeleteValue(guid);
                }
            }
            catch (Exception exc)
            {
                Console.Error.WriteLine(exc.ToString());
            }
        }

        #endregion
    }
}
